package com.blank.shop.seata.business;

import cn.hutool.core.util.RandomUtil;
import com.blank.shop.seata.ShopAlibabaSeataApplication;
import com.blank.shop.seata.domain.Order;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Profile;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.math.BigDecimal;

/**
 * 测试入口
 * <br/>Date 2021/8/30
 * <br/>Time 16:11:02
 *
 * @author _blank
 */
@Profile({ShopAlibabaSeataApplication.PROFILE_BUSINESS})
@Slf4j
@RestController
public class HomeController {

    /*
     * 访问入口1：http://localhost:20400/test-feign
     * 访问入口2：http://localhost:20400/test-rest
     *
     * 官方示例项目：
     * https://github.com/alibaba/spring-cloud-alibaba/tree/2.2.6.RELEASE/spring-cloud-alibaba-examples
     *
     * 参考文档：
     * https://github.com/alibaba/spring-cloud-alibaba/blob/2.2.6.RELEASE/spring-cloud-alibaba-examples/seata-example/readme-zh.md
     *
     * 利用此脚本 https://github.com/seata/seata/blob/v1.3.0/script/config-center/nacos/nacos-config.sh 将config.txt导入nacos
     *
     * 将 https://github.com/seata/seata/blob/v1.3.0/script/server/db/mysql.sql 导入到seata数据库
     *
     * AT模式下，将 https://github.com/seata/seata/blob/v1.3.0/script/client/at/db/mysql.sql 下的undo_log表导入到各个业务库
     *
     * spring配置文件参考 https://github.com/seata/seata/tree/v1.3.0/script/client/spring
     *
     * 修改 service.vgroupMapping.my_test_tx_group
     *
     * 修改 store.mode
     * 修改 store.db.url
     * 修改 store.db.user
     * 修改 store.db.password
     *
     * 踩坑：
     *
     * fegin中你可以有多个@RequestParam但只能有不超过一个@RequestBody。
     * https://blog.csdn.net/chengluchuang2753/article/details/96868869
     *
     * Spring Cloud 使用 FeignClient 启动报错
     * Consider defining a bean of type……
     * 注解 @EnableFeignClients 与 @ComponentScan 有冲突，两种注解都会搜索注入指定目录中的 bean 。
     * @EnableFeignClients 引入了 FeignClientsRegistrar 类，实现了 Spring 的bean 资源的加载。
     * https://www.cnblogs.com/yuansc/p/10536659.html
     */

    public static final String SERVICE_URL_ACCOUNT = "blank-shop-alibaba-seata-account";
    public static final String SERVICE_URL_ORDER = "blank-shop-alibaba-seata-order";
    public static final String SERVICE_URL_STORAGE = "blank-shop-alibaba-seata-storage";

    public static final String SERVICE_URL_ORDER_CREATE = "http://127.0.0.1:20402/order-create";
    public static final String SERVICE_URL_ORDER_UPDATE = "http://127.0.0.1:20402/order-update";
    public static final String SERVICE_URL_STORAGE_DECREASE = "http://127.0.0.1:20403/storage-decrease";
    public static final String SERVICE_URL_ACCOUNT_DECREASE = "http://127.0.0.1:20401/account-decrease";

    @Resource
    private AccountService accountService;
    @Resource
    private OrderService orderService;
    @Resource
    private StorageService storageService;

    @Resource
    private RestTemplate restTemplate;

    @GlobalTransactional(timeoutMills = 300000, name = "spring-cloud-transactional-at-rest", rollbackFor = {Exception.class})
    @GetMapping(path = "test-rest")
    public ResponseEntity<String> testRest() {
        log.info(">>>**********开始下单！");

        final Order order = this.generateOrder();
        final HttpHeaders responseHeaders = new HttpHeaders();

        // 创建订单
        HttpEntity<Order> httpEntityForOrder = new HttpEntity<>(order, responseHeaders);
        this.restTemplate.exchange(SERVICE_URL_ORDER_CREATE, HttpMethod.POST, httpEntityForOrder, String.class);

        // 扣减库存
        final MultiValueMap<String, Object> paramMapForStorage = new LinkedMultiValueMap<>();
        paramMapForStorage.add("productId", order.getProductId());
        paramMapForStorage.add("count", order.getCount());
        final HttpEntity<MultiValueMap<String, Object>> httpEntityForStorage = new HttpEntity<>(paramMapForStorage, responseHeaders);
        this.restTemplate.exchange(SERVICE_URL_STORAGE_DECREASE, HttpMethod.POST, httpEntityForStorage, String.class);

        // 扣减余额
        final MultiValueMap<String, Object> paramMapForAccount = new LinkedMultiValueMap<>();
        paramMapForAccount.add("userId", order.getUserId());
        paramMapForAccount.add("money", order.getMoney());
        final HttpEntity<MultiValueMap<String, Object>> httpEntityForAccount = new HttpEntity<>(paramMapForAccount, responseHeaders);
        this.restTemplate.exchange(SERVICE_URL_ACCOUNT_DECREASE, HttpMethod.POST, httpEntityForAccount, String.class);

        // 修改订单状态为完成状态
        order.setStatus(1);
        httpEntityForOrder = new HttpEntity<>(order, responseHeaders);
        this.restTemplate.exchange(SERVICE_URL_ORDER_UPDATE, HttpMethod.POST, httpEntityForOrder, String.class);

        log.info("<<<**********完成下单！");
        return ResponseEntity.ok("rest...");
    }

    @GlobalTransactional(timeoutMills = 300000, name = "spring-cloud-transactional-at-feign", rollbackFor = {Exception.class})
    @GetMapping(path = "test-feign")
    public ResponseEntity<String> testFeign() {
        log.info(">>>**********开始下单！");

        final Order order = this.generateOrder();


        // 创建订单
        this.orderService.create(order);

        // 扣减库存
        this.storageService.decrease(order.getProductId(), order.getCount());

        // 扣减余额
        this.accountService.decrease(order.getUserId(), order.getMoney());

        // 修改订单状态为完成状态
        order.setStatus(1);
        this.orderService.update(order);


        log.info("<<<**********完成下单！");
        return ResponseEntity.ok("feign...");
    }

    private Order generateOrder() {
        final Order order = new Order();
        order.setUserId(100L);
        order.setProductId(31415L);
        order.setCount(RandomUtil.randomInt(20));
        order.setMoney(new BigDecimal(String.valueOf(RandomUtil.randomDouble(100D, 999D))));
        // 创建中
        order.setStatus(0);
        return order;
    }

}
